﻿/*
Copyright (c) 2012 Bill Davidsen (wdavidsen@yahoo.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

(function (window) {

    if (!window.SCS)
        window.SCS = {};

    SCS.Splitter = function (root, settings) {

        if (typeof (root) == "undefined")
            throw new Error("A root element must be specified");

        if (typeof (settings) == "undefined")
            settings = {};

        this.root = root;
        this._isExpanded = typeof (settings.expanded) != "undefined" ? settings.expanded : true;
        this._leftPosition = typeof (settings.leftPosition) != "undefined" ? settings.leftPosition : 300;
        this._leftSnap = typeof (settings.leftSnap) != "undefined" ? settings.leftSnap : 30;
        this._leftMinWidth = typeof (settings.leftMinWidth) != "undefined" ? settings.leftMinWidth : 0;
        this._leftMaxWidth = typeof (settings.leftMaxWidth) != "undefined" ? settings.leftMaxWidth : -1;
        this._redrawWhileDragging = typeof (settings.redrawWhileDragging) != "undefined" ? settings.redrawWhileDragging : false;

        this._isInInit = true;
        this._dragging = false;
        this._prevLeftPosition = null;

        this._paneContainer = null;
        this._leftPane = null;
        this._rightPane = null;
        this._divider = null;
        this._stateHolder = null;

        this._mouseUpHandler = null;
        this._mouseDownHandler = null;
        this._doubleClickHandler = null;

        this._dragHandler = null;
        this._selectStartHandler = null;

        this._docMouseUpHandler = null;
        this._docMouseMoveHandler = null;
        this._docKeyPressHandler = null;
        this._resizeHandler = null;

        this._events = {};

        this.init();
    };

    SCS.Splitter.prototype = {
        init: function () {

            this._paneContainer = this._getFirstChild(this.root);
            this._leftPane = this._getFirstChild(this._paneContainer);
            this._divider = this._getNextSibling(this._leftPane);
            this._rightPane = this._getNextSibling(this._divider);
            this._stateHolder = this._getNextSibling(this._rightPane);

            this._docKeyPressHandler = this._proxy(this._onDocKeyPress, this);
            document.addEventListener("keypress", this._docKeyPressHandler, false);

            this._mouseUpHandler = this._proxy(this._onMouseUp, this);
            this._divider.addEventListener("mouseup", this._mouseUpHandler, false);

            this._mouseDownHandler = this._proxy(this._onMouseDown, this);
            this._divider.addEventListener("mousedown", this._mouseDownHandler, false);

            this._doubleClickHandler = this._proxy(this._onDoubleClick, this);
            this._divider.addEventListener("dblclick", this._doubleClickHandler, false);

            this._dragHandler = this._proxy(this._onDrag, this);
            document.body.addEventListener("drag", this._dragHandler, false);

            this._selectStartHandler = this._proxy(this._onStart, this);
            document.body.addEventListener("selectstart", this._selectStartHandler, false);

            this._resizeHandler = this._proxy(this._onResize, this);
            window.addEventListener("resize", this._resizeHandler, false);

            this._onResize();
            this._redaw(this._leftPosition);

            this._isInInit = false;
        },
        dispose: function () {

            try {
                document.removeEventListener("keypress", this._docKeyPressHandler, false);
                this._docKeyPressHandler = null;

                this._divider.removeEventListener("mouseup", this._mouseUpHandler, false);
                this._mouseUpHandler = null;

                this._divider.removeEventListener("mousedown", this._mouseDownHandler, false);
                this._mouseDownHandler = null;

                this._divider.removeEventListener("dblclick", this._doubleClickHandler, false);
                this._doubleClickHandler = null;

                document.body.removeEventListener("drag", this._dragHandler, false);
                this._dragHandler = null;

                document.body.removeEventListener("selectstart", this._selectStartHandler, false);
                this._selectStartHandler = null;

                window.removeEventListener("resize", this._resizeHandler, false);
                this._resizeHandler = null;
            }
            catch (e) {

            }
        },
        _onMouseUp: function (e) {

            this._stopDragging();
        },
        _onMouseDown: function (e) {

            e.preventDefault();

            if (this._dragging) {
                this._stopDragging();
                return;
            }

            this._dragging = true;
            this._divider.parrentOffsetX = this._getElementLeft(this._divider.parentNode) + e.clientX - this._getElementLeft(this._divider);

            this._docMouseMoveHandler = this._proxy(this._onDocMouseMove, this);
            document.addEventListener("mousemove", this._docMouseMoveHandler, false);

            this._docMouseUpHandler = this._proxy(this._onDocMouseUp, this);
            document.addEventListener("mouseup", this._docMouseUpHandler, false);
        },
        _onDocKeyPress: function (e) {

            if (e.charCode == 116) {
                var target = e.target;

                if (target.tagName.toLowerCase() != "input" && target.tagName.toLowerCase() != "textarea")
                    this.toggleDivider();

                e.stopPropagation();
            }
            else if (e.charCode == 27) {

                this._stopDragging();
            }
        },
        _onDoubleClick: function (e) {
            this.toggleDivider();
        },
        _onDrag: function (e) {
            return !this._dragging;
        },
        _onStart: function (e) {
            return !this._dragging;
        },
        _onDocMouseUp: function (e) {
            this._onMouseUp(e);
        },
        _onDocMouseMove: function (e) {
            this._mouseMove(e);
        },
        _onResize: function (e) {

            if (document.all) {

                var ver = this._getIeVersion();

                if (ver >= 7 && ver < 8) {

                    if (this._paneContainer.offsetHeight == document.documentElement.clientHeight)
                        this._paneContainer.style.height = document.documentElement.clientHeight;
                    else
                        this._paneContainer.style.height = this.get_element().offsetHeight;
                }
                //else if (ver >= 6 && ver < 7) {
                // handle ie6 issues here
                //}
            }
        },
        _updateState: function () {

            if (this._stateHolder)
                this._stateHolder.value = String.format("{0},{1}", this._leftPosition, this._isExpanded);
        },
        toggleDivider: function () {

            var eventArgs = { expanded: this._isExpanded, cancel: false };
            this.raiseToggling(eventArgs);

            if (eventArgs.cancel)
                return;

            this._openCollapse();

            this.raiseToggled({});
        },

        _stopDragging: function () {

            if (this._docMouseMoveHandler) {
                document.removeEventListener("mousemove", this._docMouseMoveHandler, false);
                this._docMouseMoveHandler = null;
            }

            if (this._docMouseUpHandler) {
                document.removeEventListener("mouseup", this._docMouseUpHandler, false);
                this._docMouseUpHandler = null;
            }

            this._dragging = false;
            var leftPixels = this._divider.offsetLeft;

            var eventArgs = { leftWidth: leftPixels, cancel: false };
            this.raiseResizing(eventArgs);

            if (eventArgs.cancel)
                leftPixels = this._leftPosition;

            this._redaw(leftPixels);

            if (!eventArgs.cancel)
                this.raiseResized({});
        },

        _redaw: function (width) {

            if (this._leftMinWidth < 1 && width <= this._leftSnap) {
                this._leftPane.style.display = "none";
                this._divider.style.left = "0px";
                this._isExpanded = false;
            }
            else {
                this._leftPane.style.display = (width > 0) ? "block" : "none";
                this._divider.style.left = width + "px";
                this._leftPane.style.width = width + "px";
                this._isExpanded = true;
            }

            this._prevLeftPosition = this._leftPosition;
            this._leftPosition = width;
            this._updateState();
        },
        _mouseMove: function (e) {

            var pixels = e.clientX - this._divider.parrentOffsetX;

            if (this._leftMinWidth < 1 && pixels <= this._leftSnap) {
                pixels = 0;
            }
            else if (pixels <= this._leftMinWidth) {
                pixels = this._leftMinWidth;
            }
            else if (this._leftMaxWidth != -1 && pixels >= this._leftMaxWidth) {
                pixels = this._leftMaxWidth;
            }

            var eventArgs = { leftWidth: pixels, cancel: false };
            this.raiseDragging(eventArgs);

            if (eventArgs.cancel)
                return;

            this._divider.style.left = pixels + "px";

            if (this._redrawWhileDragging)
                this._redaw(this._divider.offsetLeft);

            return false;
        },
        _getFirstChild: function (obj) {

            obj = obj.firstChild;
            while (obj && obj.nodeType != 1)
                obj = obj.nextSibling;

            return obj;
        },
        _getNextSibling: function (obj) {

            obj = obj.nextSibling;
            while (obj && obj.nodeType != 1)
                obj = obj.nextSibling;

            return obj;
        },
        _getElementLeft: function (obj) {
            var x = 0;

            while (obj) {
                x += obj.offsetLeft;
                obj = obj.offsetParent;
            }
            return x;
        },
        _openCollapse: function () {

            this._redaw((this._divider.offsetLeft == 0) ? this._prevLeftPosition : 0);
        },

        _getIeVersion: function () {

            var version = -1,
                expression = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");

            if (expression.exec(navigator.userAgent) != null) {
                version = parseFloat(RegExp.$1);
            }

            return version;
        },

        _trace: function (msg) {

            var output = $get("splitter-trace");

            if (output)
                output.innerHTML = msg + "<br/><br/>" + output.innerHTML;
        },

        _proxy: function (func, context) {

            return function () {
                return func.apply(context, arguments);
            }
        },

        changeTopPosition: function (value) {

            if (!value.toString().toLowerCase().endsWith("px"))
                value = value + "px";

            this.get_element().style.top = value;
        },

        // Toggle Events
        add_toggling: function (handler) {

            this._events["toggling"] = handler;
        },
        remove_toggling: function (handler) {

            this._events["toggling"] = null;
        },
        raiseToggling: function (eventArgs) {

            var handler = this._events["toggling"];
            if (handler)
                handler(this, eventArgs);
        },
        add_toggled: function (handler) {

            this._events["toggled"] = handler;
        },
        remove_toggled: function (handler) {

            this._events["toggled"] = null;
        },
        raiseToggled: function (eventArgs) {

            var handler = this._events["toggled"];
            if (handler)
                handler(this, eventArgs);
        },
        // Dragging Event
        add_dragging: function (handler) {

            this._events["dragging"] = handler;
        },
        remove_dragging: function (handler) {

            this._events["dragging"] = null;
        },
        raiseDragging: function (eventArgs) {

            var handler = this._events["dragging"];
            if (handler)
                handler(this, eventArgs);
        },
        // Resize Events
        add_resizing: function (handler) {

            this._events["resizing"] = handler;
        },
        remove_resizing: function (handler) {

            this._events["resizing"] = null;
        },
        raiseResizing: function (eventArgs) {

            var handler = this._events["resizing"];
            if (handler)
                handler(this, eventArgs);
        },
        add_resized: function (handler) {

            this._events["resized"] = handler;
        },
        remove_resized: function (handler) {

            this._events["resized"] = null;
        },
        raiseResized: function (eventArgs) {

            var handler = this._events["resized"];
            if (handler)
                handler(this, eventArgs);
        },
        // Properties
        get_leftPosition: function () {
            return this._leftPosition;
        },
        set_leftPosition: function (value) {

            if (!this._isInInit && (value != this._leftPosition) && (value < this._leftMaxWidth) && (value > this._leftMinWidth))
                this._redaw(value);

            this._prevLeftPosition = this._leftPosition;
            this._leftPosition = value;
        },
        get_leftSnap: function () {
            return this._leftSnap;
        },
        set_leftSnap: function (value) {
            this._leftSnap = value;
        },
        get_leftMinWidth: function () {
            return this._leftMinWidth;
        },
        set_leftMinWidth: function (value) {
            this._leftMinWidth = value;
        },
        get_leftMaxWidth: function () {
            return this._leftMaxWidth;
        },
        set_leftMaxWidth: function (value) {
            this._leftMaxWidth = value;
        },
        set_isExpanded: function (value) {

            if (!this._isInInit && value != this._isExpanded) {
                this._openCollapse();
            }

            this._isExpanded = value;
        },
        get_isExpanded: function () {
            return this._isExpanded;
        },
        get_redrawWhileDragging: function () {
            return this._redrawWhileDragging;
        },
        set_redrawWhileDragging: function (value) {
            this._redrawWhileDragging = value;
        }
    };

})(window);
